/*
	Simple example using restinio::transforms::zlib facility.
*/

#include <iostream>
#include <random>

#include <restinio/core.hpp>
#include <restinio/transforms/zlib.hpp>

#include <fmt/format.h>

#include <restinio-helpers/cmd_line_args_helpers.hpp>

//
// app_args_t
//

struct app_args_t
{
	bool m_help{ false };
	std::string m_address{ "localhost" };
	std::uint16_t m_port{ 8080 };
	std::size_t m_pool_size{ 1 };
	bool m_trace_server{ false };

	static app_args_t
	parse( int argc, const char * argv[] )
	{
		using namespace restinio_helpers;

		app_args_t result;

		process_cmd_line_args( argc, argv, result,
				cmd_line_arg_t{
						result.m_address,
						"-a", "--address",
						"address to listen (default: {})"
					},
				cmd_line_arg_t{
						result.m_port,
						"-p", "--port",
						"port to listen (default: {})"
					},
				cmd_line_arg_t{
						result.m_pool_size,
						"-n", "--thread-pool-size",
						"size of a thread pool to run server (default: {})"
					},
				cmd_line_arg_t{
						result.m_trace_server,
						"-t", "--trace",
						"enable trace server"
					} );

		return result;
	}
};

using router_t = restinio::router::express_router_t<>;

namespace rtz = restinio::transforms::zlib;

auto make_router()
{
	auto router = std::make_unique< router_t >();

	router->http_post(
		R"-(/)-",
		[ & ]( const restinio::request_handle_t& req, auto ){

			return req->create_response()
				.append_header( "Server", "RESTinio" )
				.append_header_date_field()
				.append_header(
					// Inherit content-type from request (if it is set).
					restinio::http_field::content_type,
					req->header().get_field_or(
						restinio::http_field::content_type,
						"text/plain" ) )
				.set_body(
					// Decompress request body if necessary.
					rtz::handle_body(
						*req,
						// NOTE: the specification of the return type is necessary
						// for Visual Studio 2019 (16.11.5) and C++20.
						[]( std::string body_data ) -> std::string
						{
							return body_data;
						} ) )
				.done();

		} );

	return router;
}

template < typename Server_Traits >
void run_server( const app_args_t & args )
{
	restinio::run(
		restinio::on_thread_pool< Server_Traits >( args.m_pool_size )
			.port( args.m_port )
			.address( args.m_address )
			.concurrent_accepts_count( args.m_pool_size )
			.request_handler( make_router() ) );
}

int main( int argc, const char * argv[] )
{
	try
	{
		const auto args = app_args_t::parse( argc, argv );

		if( !args.m_help )
		{
			if( args.m_trace_server )
			{
				using traits_t =
					restinio::traits_t<
						restinio::asio_timer_manager_t,
						restinio::shared_ostream_logger_t,
						router_t >;

				run_server< traits_t >( args );
			}
			else
			{
				using traits_t =
					restinio::traits_t<
						restinio::asio_timer_manager_t,
						restinio::null_logger_t,
						router_t >;

				run_server< traits_t >( args );
			}
		}
	}
	catch( const std::exception & ex )
	{
		std::cerr << "Error: " << ex.what() << std::endl;
		return 1;
	}

	return 0;
}
